#include "stdafx.h"
#include "SensorReader.h"
#include <memory>
#include <windows.h>
#include <regex>
#include "ISensorReaderListener.h"
#include "SerialPortReader.h"
#include "ISensorReader.h"
#include "IReading.h"
#include "Reading.h"
#include "Time.h"

#include <iostream>



namespace Sensor {

	DWORD WINAPI runSensorReaderThread(LPVOID instance) {
		SensorReader* sensorReader = (SensorReader*)instance;
		while (true) {
			Sleep(100); // FIXME: this safe and good idea?
			sensorReader->update();
		}
	}

	void SensorReader::update() {
		std::string line = serialPortReader.readLine();
		std::list<std::unique_ptr<IReading>> readings = parseInputString(line);

		// Notify listeners
		for (std::list<ISensorReaderListener*>::iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); listenerIter++) {
			ISensorReaderListener* listener = *listenerIter;
			for (std::list<std::unique_ptr<IReading>>::iterator readingIter = readings.begin(); readingIter != readings.end(); readingIter++) {
				unique_ptr<IReading> reading = (*readingIter)->copy();
				listener->onReading(reading);
			}
		}
	}

	std::list<unique_ptr<IReading>> SensorReader::parseInputString(std::string inputString) const {
		std::list<std::unique_ptr<IReading>> readings;

		std::regex parsingRegex("\\d+");
		std::regex readingRegex("\\s*NODE:\\s*\\d+\\s*BATTERY:\\s*\\d+\\s*TEMP:\\s*\\d+\\s*DAMPNESS:\\s*\\d+\\s*");
		std::regex networkRegex("\\s*NODE:\\s*\\d+\\s*is\\s*following\\s*NODE:\\s*\\d+\\s*");

		if (std::regex_match(inputString, readingRegex)) {
			std::cout << "Reading: " << inputString <<  std::endl;

			int sensorId = -1;
			double battery = -1; // NaN
			double temperature = -1; // NaN
			double moisture = -1; // NaN

			int count = 0;
			std::smatch match;
			std::string parsingStr(inputString);
			while (std::regex_search(parsingStr, match, parsingRegex)) {
				switch (count) {
				case 0:
					sensorId = atoi(match.str().c_str());
					break;
				case 1:
					battery = atof(match.str().c_str());
					break;
				case 2:
					temperature = atof(match.str().c_str());
					break;
				case 3:
					moisture = atof(match.str().c_str());
					break;
				}
				parsingStr = match.suffix().str(); // Whatever is beyond the match
				count++;
			}

			readings.push_back(
				unique_ptr<IReading>(new Reading(sensorId, unique_ptr<ITime>(new Time()), "Battery", battery))
				);
			readings.push_back(
				unique_ptr<IReading>(new Reading(sensorId, unique_ptr<ITime>(new Time()), "Temperature", temperature))
				);
			readings.push_back(
				unique_ptr<IReading>(new Reading(sensorId, unique_ptr<ITime>(new Time()), "Moisture", moisture))
				);
		} else if (std::regex_match(inputString, networkRegex)) {
			std::cout << "Network: " << inputString <<  std::endl;

			int data[2];
			std::smatch match;
			std::string parsingStr(inputString);

			for (int i=0; i<2; i++) {
				std::regex_search(parsingStr, match, parsingRegex);
				data[i] = atoi(match.str().c_str());
				parsingStr = match.suffix().str(); // Whatever is beyond the match
			}

			int sensorId = data[0];
			int parentId = data[1];

			readings.push_back(
				unique_ptr<IReading>(new Reading(sensorId, unique_ptr<ITime>(new Time()), "Parent", parentId))
				);
		} else {
			std::cout << "Malformed serial input string." << std::endl;
		}

		return readings;
	}

	SensorReader::SensorReader() : serialPortReader(1024) {
		// Get the serial port being used by MSP430 UART
		DWORD size = 256;
		char buffer[256];

		if (SHGetValue(HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\SERIALCOMM\\", "\\Device\\USBSER000", nullptr, buffer, &size) != ERROR_SUCCESS) {
			throw std::exception("Could not find the device. Ensure the device is connected and that the device drivers have been installed correctly.");
		}

		serialPortReader.open(std::string(buffer));
	}

	SensorReader::~SensorReader() {
		stop();
	}

	void SensorReader::start() {
		DWORD threadId;
		this->thread = CreateThread( 
            nullptr,                   // default security attributes
            0,                      // use default stack size  
            runSensorReaderThread,          // thread function name
            this,					// argument to thread function 
            0,                      // use default creation flags 
            &threadId);   // returns the thread identifier 
	}

	void SensorReader::stop() {
		TerminateThread(thread, 0); // FIXME: probably want to gently join the thread instead, to release resources
	}

	void SensorReader::addListener(ISensorReaderListener* listener) {
		this->listeners.push_back(listener);
	}
	
	void SensorReader::removeListener(ISensorReaderListener* listener) {
		this->listeners.remove(listener);
	}

}